Coverage Report

Created: 2025-05-07 21:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\tools.proto\tools.proto\compiler\src\compiler\union.rs
Line
Count
Source
1
// Copyright (c) 2024, BlockProject 3D
2
//
3
// All rights reserved.
4
//
5
// Redistribution and use in source and binary forms, with or without modification,
6
// are permitted provided that the following conditions are met:
7
//
8
//     * Redistributions of source code must retain the above copyright notice,
9
//       this list of conditions and the following disclaimer.
10
//     * Redistributions in binary form must reproduce the above copyright notice,
11
//       this list of conditions and the following disclaimer in the documentation
12
//       and/or other materials provided with the distribution.
13
//     * Neither the name of BlockProject 3D nor the names of its contributors
14
//       may be used to endorse or promote products derived from this software
15
//       without specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
use crate::compiler::message::{Referenced, SizeInfo};
30
use crate::compiler::structure::{Field, FieldType, FieldView, FixedField, Structure};
31
use crate::compiler::util::objects::name_index;
32
use crate::compiler::{Error, Protocol};
33
use crate::model::protocol::Description;
34
use std::rc::Rc;
35
36
#[derive(Clone, Debug)]
37
pub struct UnionField {
38
    pub name: String,
39
    pub description: Option<Description>,
40
    pub case: usize,
41
    pub item_type: Option<Referenced>,
42
}
43
44
impl UnionField {
45
100
    pub fn from_model(
46
100
        proto: &Protocol,
47
100
        discriminant: &FixedField,
48
100
        value: crate::model::union::UnionField,
49
100
    ) -> Result<Self, Error> {
50
100
        let case: usize = match &discriminant.view {
51
0
            FieldView::Float { .. } => return Err(Error::FloatInUnionDiscriminant),
52
84
            FieldView::Enum(r) => {
53
84
                r.variants_map.get(&value.case).copied().ok_or(Error::InvalidUnionCase(value.case))
?0
as usize
54
            }
55
            //TODO: Check how unions work with the new bits-type/raw-type/view-type separation
56
16
            FieldView::None => value.case.parse().map_err(|_| Error::InvalidUnionCase(
value.case0
))
?0
,
57
        };
58
100
        let item_type = value
59
100
            .item_type
60
100
            .map(|v| 
Referenced::lookup78
(
proto78
,
&v78
).
ok_or78
(
Error::UndefinedReference(v)78
))
61
100
            .transpose()
?0
;
62
100
        Ok(UnionField {
63
100
            name: value.name,
64
100
            description: value.description,
65
100
            case,
66
100
            item_type,
67
100
        })
68
100
    }
69
}
70
71
#[derive(Clone, Debug)]
72
pub struct DiscriminantField {
73
    pub root: Rc<Structure>,
74
    pub leaf: Rc<Structure>,
75
    pub leaf_index: usize,
76
    pub index_list: Vec<usize>,
77
}
78
79
struct DiscriminantFieldIterator<'a> {
80
    cur: &'a Structure,
81
    index: std::slice::Iter<'a, usize>,
82
}
83
84
impl<'a> Iterator for DiscriminantFieldIterator<'a> {
85
    type Item = (&'a Field, bool);
86
87
28
    fn next(&mut self) -> Option<Self::Item> {
88
28
        let 
index16
= self.index.next()
?12
;
89
16
        let field = &self.cur.fields[*index];
90
16
        let is_leaf = match &field.ty {
91
12
            FieldType::Fixed(_) => true,
92
0
            FieldType::Array(_) => std::unreachable!(),
93
4
            FieldType::Struct(v) => {
94
4
                self.cur = v;
95
4
                false
96
            }
97
        };
98
16
        Some((field, is_leaf))
99
28
    }
100
}
101
102
impl DiscriminantField {
103
12
    pub fn iter(&self) -> impl Iterator<Item = (&Field, bool)> {
104
12
        DiscriminantFieldIterator {
105
12
            cur: &self.root,
106
12
            index: self.index_list.iter(),
107
12
        }
108
12
    }
109
110
0
    pub fn get_leaf(&self) -> &Field {
111
0
        &self.leaf.fields[self.leaf_index]
112
0
    }
113
114
106
    pub fn get_leaf_fixed(&self) -> &FixedField {
115
106
        self.leaf.fields[self.leaf_index].ty.as_fixed().unwrap()
116
106
    }
117
118
22
    pub fn from_model(proto: &Protocol, discriminant: String) -> Result<Self, Error> {
119
22
        let mut parts = discriminant.split(".");
120
22
        let name = parts.next().ok_or(Error::InvalidUnionDiscriminant)
?0
;
121
22
        let mut leaf = proto.structs.get(name).ok_or_else(|| Error::UndefinedReference(
name0
.
into0
()))
?0
;
122
22
        let root = leaf;
123
22
        let mut index_list = Vec::new();
124
28
        for sub in parts {
125
28
            let (index, field) = leaf
126
28
                .fields
127
28
                .iter()
128
28
                .enumerate()
129
28
                .find(|(_, v)| v.name == sub)
130
28
                .ok_or_else(|| Error::UndefinedReference(
format!0
(
"{}.{}"0
, name, sub)))
?0
;
131
28
            index_list.push(index);
132
28
            match &field.ty {
133
22
                FieldType::Fixed(_) => break,
134
6
                FieldType::Struct(v) => leaf = v,
135
0
                FieldType::Array(_) => return Err(Error::InvalidUnionDiscriminant),
136
            }
137
        }
138
22
        Ok(DiscriminantField {
139
22
            root: root.clone(),
140
22
            leaf: leaf.clone(),
141
22
            leaf_index: index_list.last().copied().unwrap(),
142
22
            index_list,
143
22
        })
144
22
    }
145
}
146
147
#[derive(Clone, Debug)]
148
pub struct Union {
149
    pub name: String,
150
    pub discriminant: DiscriminantField,
151
    pub description: Option<Description>,
152
    pub cases: Vec<UnionField>,
153
    pub size: SizeInfo,
154
}
155
156
impl Union {
157
13
    pub fn has_content(&self) -> bool {
158
22
        
self.cases.iter()13
.
any13
(|v| v.item_type.is_some())
159
13
    }
160
161
22
    pub fn from_model(proto: &Protocol, value: crate::model::union::Union) -> Result<Self, Error> {
162
22
        let discriminant = DiscriminantField::from_model(proto, value.discriminant)
?0
;
163
22
        let cases = value
164
22
            .cases
165
22
            .into_iter()
166
100
            .
map22
(|v| UnionField::from_model(proto, discriminant.get_leaf_fixed(), v))
167
22
            .collect::<Result<Vec<UnionField>, Error>>()
?0
;
168
100
        let 
is_element_dyn_sized22
=
cases.iter()22
.
any22
(|v| {
169
100
            v.item_type
170
100
                .as_ref()
171
100
                .map(|v| match 
v78
{
172
66
                    Referenced::Struct(_) => false,
173
12
                    Referenced::Message(v) => v.size.is_element_dyn_sized,
174
78
                })
175
100
                .unwrap_or_default()
176
100
        });
177
40
        let 
is_dyn_sized22
=
cases.iter()22
.
any22
(|v| {
178
40
            v.item_type
179
40
                .as_ref()
180
40
                .map(|v| match 
v18
{
181
6
                    Referenced::Struct(_) => false,
182
12
                    Referenced::Message(v) => v.size.is_dyn_sized,
183
18
                })
184
40
                .unwrap_or_default()
185
40
        });
186
22
        Ok(Union {
187
22
            name: value.name,
188
22
            discriminant,
189
22
            description: value.description,
190
22
            cases,
191
22
            size: SizeInfo {
192
22
                is_element_dyn_sized,
193
22
                is_dyn_sized,
194
22
            },
195
22
        })
196
22
    }
197
}
198
199
name_index!(Union => name);